package com.atguigu.tingshu.common.rabbit.config;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.common.rabbit.entity.GuiguCorrelationData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class RabbitInitConfigApplicationListener implements ApplicationListener<ApplicationReadyEvent> {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        // 1.初始化阶段：系统启动完成后自动执行初始化
        this.setupCallbacks();
    }

    private void setupCallbacks() {
        // 注册Exchange确认监听器(ConfirmCallback)
        this.rabbitTemplate.setConfirmCallback((correlationData, ack, reason) -> {
            if (ack) {
                // 2.Exchange到达判断-成功到达：记录成功日志
                log.info("消息发送到Exchange成功：{}", correlationData);
                // 继续后续Queue检查（由ReturnsCallback处理）
            } else {
                // 2.Exchange到达判断-到达失败：记录失败原因并立即执行重发流程
                log.error("消息发送到Exchange失败：{}", reason);
                this.retrySendMsg(correlationData);
            }
        });

        // 注册Queue退回监听器(ReturnsCallback)
        this.rabbitTemplate.setReturnsCallback(returned -> {
            // 3.Queue路由判断-路由失败：记录错误消息
            log.error("Returned: " + returned.getMessage() + "\nreplyCode: " + returned.getReplyCode()
                    + "\nreplyText: " + returned.getReplyText() + "\nexchange/rk: "
                    + returned.getExchange() + "/" + returned.getRoutingKey());

            // 3.Queue路由判断-路由失败：从redis获取相应的数据
            String redisKey = returned.getMessage().getMessageProperties().getHeader("spring_returned_message_correlation");
            String correlationDataStr = (String) redisTemplate.opsForValue().get(redisKey);
            GuiguCorrelationData guiguCorrelationData = JSON.parseObject(correlationDataStr, GuiguCorrelationData.class);

            // 3.Queue路由判断-检查消息类型
            if(guiguCorrelationData.isDelay()){
                // 延迟消息就不重发
                return;
            }
            // 普通消息就重发
            this.retrySendMsg(guiguCorrelationData);
        });
    }

    /**
            * 消息重新发送
     */
    private void retrySendMsg(CorrelationData correlationData) {
        // 获取相关数据
        GuiguCorrelationData gmallCorrelationData = (GuiguCorrelationData) correlationData;

        // 4.重发时首先进行重试次数的判断
        int retryCount = gmallCorrelationData.getRetryCount();
        if (retryCount >= 3) {
            // 大于等于3则进行管理员的通知
            log.error("生产者超过最大重试次数，将失败的消息存入数据库用人工处理；给管理员发送邮件；给管理员发送短信；");
            return;
        }

        // 不大于等于3就增加一次
        retryCount += 1;
        gmallCorrelationData.setRetryCount(retryCount);
        // 把重试次数给重新赋值，然后把数据存储到redis中，过期时间10分钟
        redisTemplate.opsForValue().set(gmallCorrelationData.getId(), JSON.toJSONString(gmallCorrelationData), 10, TimeUnit.MINUTES);
        log.info("进行消息重发！");

        // 5.对消息的类型进行判断
        if (gmallCorrelationData.isDelay()) {
            // 延迟消息，则获取路由器，路由键，把消息设置一下
            rabbitTemplate.convertAndSend(gmallCorrelationData.getExchange(), gmallCorrelationData.getRoutingKey(), gmallCorrelationData.getMessage(), message -> {
                message.getMessageProperties().setDelay(gmallCorrelationData.getDelayTime() * 1000);
                return message;
            }, gmallCorrelationData);
        } else {
            // 普通消息就不用设置
            rabbitTemplate.convertAndSend(gmallCorrelationData.getExchange(), gmallCorrelationData.getRoutingKey(), gmallCorrelationData.getMessage(), gmallCorrelationData);
        }
    }
}